#pragma once
#include "../comm/Log.h"
#include "../comm/util.h"
#include "../comm/msg.pb.h"
#include <unordered_map>
#include <memory>
#include <mutex>

namespace ns_exchange
{
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_proto;

    // 交换机数据描述
    struct Exchange
    {
        using ptr = shared_ptr<Exchange>;

        string _name;                                          // 名称
        ExchangeType _type;                                    // 类型
        bool _IsDurable;                                       // 交换机持久化标志
        bool _auto_delete;                                     // 自动删除标志
        google::protobuf::Map<std::string, std::string> _args; // 其它参数

        Exchange() {}

        Exchange(const string &name, const ExchangeType &type, const bool &IsDurable,
                 const bool &auto_delete,
                 google::protobuf::Map<std::string, std::string> args)
            : _name(name), _type(type), _IsDurable(IsDurable),
              _auto_delete(auto_delete), _args(args)
        {
        }

        // 将数据库格式(key=val&key=val.....)的字符串解析到unordered_map里
        void set_args(const string &args)
        {
            int begin = 0, end = 0;
            while (begin < args.size())
            {
                end = args.find('=', begin);
                string key = args.substr(begin, end - begin);
                begin = end + 1;

                end = args.find('&', begin);
                string val = args.substr(begin, end - begin);
                begin = end + 1;

                _args[key] = val; // 数据存储

                if (end == string::npos)
                    break;
            }
        }
        // 将unordered_map里的数据取出来组织成数据库格式
        string get_args()
        {
            if (_args.empty())
                return "";
            string args;
            for (const auto &p : _args)
            {
                args += p.first;
                args += '=';
                args += p.second;
                args += '&';
            }
            args.pop_back();
            return args;
        }
    };

    // 交换机持久化管理(将交换机数据存储到数据库中)
    class ExchangeDurable
    {
    public:
        ExchangeDurable(const string &dbfile)
            : _sql_helper(dbfile)
        {
            // 如果给的是一个路径且父级路径不存在时
            string path = FileOper::parentdir(dbfile);
            FileOper::createdir(path);
            assert(_sql_helper.open());

            // 创建表
            create_table();
        }

        // 创建、删除交换机数据表
        void create_table()
        {
            string CREATE_TABLE = "create table if not exists exchange_table(\
            name varchar(32) primary key, type int, durable int, auto_delete int, \
            args varchar(64));";
            bool ret = _sql_helper.exec(CREATE_TABLE, nullptr, nullptr);
            if (!ret)
            {
                LOG(WARNING) << "创建交换机数据表失败" << endl;
                abort(); // 异常退出
            }
        }
        void delete_table()
        {
            string DROP_TABLE = "drop table if exists exchange_table;";
            bool ret = _sql_helper.exec(DROP_TABLE, nullptr, nullptr);
            if (!ret)
            {
                LOG(WARNING) << "删除交换机数据表失败" << endl;
                abort(); // 异常退出
            }
        }

        // 增加、删除指定交换机
        void insert_exchange(const Exchange::ptr &exchange)
        {
            string INSERT_SQL = "insert into exchange_table values('%s', %d, %d, %d, '%s');";
            char buffer[1024] = {0};
            string args = exchange->get_args();
            sprintf(buffer, INSERT_SQL.c_str(), exchange->_name.c_str(), exchange->_type,
                    exchange->_IsDurable, exchange->_auto_delete, args.c_str());
            bool ret = _sql_helper.exec(buffer, nullptr, nullptr);
            if (!ret)
            {
                LOG(WARNING) << "增加指定交换机失败" << endl;
                return;
            }
        }
        void delete_exchange(const string &name)
        {
            // string DELETE_SQL = "delete from exchange_table where name='%s';";
            stringstream ss;
            ss << "delete from exchange_table where name=";
            ss << "'" << name << "';";
            bool ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if (!ret)
            {
                LOG(WARNING) << "删除指定交换机失败" << endl;
                return;
            }
        }

        // 查询(恢复)所有的交换机数据
        unordered_map<string, Exchange::ptr> recovery_all() // second类型采用指针提高管理
        {
            unordered_map<string, Exchange::ptr> ret;
            string SQL = "select * from exchange_table;";
            _sql_helper.exec(SQL, recovery, &ret);

            return ret;
        }

    private:
        static int recovery(void *args, int col, char **row_data, char **field_name)
        {
            // 传来的容器参数进行强转
            using exchange_map = unordered_map<string, Exchange::ptr>;
            exchange_map *ret = static_cast<exchange_map *>(args);

            // 创建交换机对象指针
            shared_ptr<Exchange> exp = make_shared<Exchange>();

            // 交换机数据填充
            exp->_name = row_data[0];
            exp->_type = static_cast<ExchangeType>(atoi(row_data[1]));
            exp->_IsDurable = static_cast<bool>(atoi(row_data[2]));
            exp->_auto_delete = static_cast<bool>(atoi(row_data[3]));
            if (row_data[4]) // 判断数据是否存在
                exp->set_args(row_data[4]);

            ret->insert(make_pair(exp->_name, exp)); // 插入数据

            return SQLITE_OK;
        }

    private:
        SqliteHelper _sql_helper; // 数据库句柄
    };

    //----------------------------------------------------------------------------
    // 交换机管理方法(对外提供)
    class ExchangeManage
    {
    public:
        ExchangeManage(const string &dbfile)
            : _exchange_durable(dbfile)
        {
            // 构造的时候就进行数据恢复
            _exchanges = _exchange_durable.recovery_all();
        }

        // 声明(创建)一个交换机
        void declare_exchange(const string &name, const ExchangeType &type,
                              const bool &IsDurable,
                              const bool &auto_delete,
                              const google::protobuf::Map<std::string, std::string> &args)
        {
            unique_lock<mutex> lock(_mtx);
            if (_exchanges.count(name))
            {
                LOG(WARNING) << "该交换机名称已经存在" << endl;
                return;
            }
            auto exp = make_shared<Exchange>(name, type, IsDurable, auto_delete, args);
            if (IsDurable == true)
            {
                _exchange_durable.insert_exchange(exp);
            }
            _exchanges.insert(make_pair(name, exp));
        }

        // 删除指定交换机
        void delete_exchange(const string &name)
        {
            unique_lock<mutex> lock(_mtx);
            if (!_exchanges.count(name))
            {
                LOG(WARNING) << "该交换机不存在" << endl;
                return;
            }
            if (_exchanges[name]->_IsDurable) // 判断库中是否存有该交换机数据
                _exchange_durable.delete_exchange(name);
            _exchanges.erase(name);
        }

        // 获取指定交换机数据
        Exchange::ptr select_exchange(const string &name)
        {
            unique_lock<mutex> lock(_mtx);
            if (!_exchanges.count(name))
            {
                LOG(WARNING) << "该交换机不存在" << endl;
                return Exchange::ptr();
            }
            return _exchanges[name];
        }

        //----------------------------------------以下用于测试
        // 判断交换机是否存在(供测试使用)
        bool exist(const string &name)
        {
            unique_lock<mutex> lock(_mtx);
            return _exchanges.count(name);
        }

        // 清除交换机(供测试使用)
        void clear()
        {
            unique_lock<mutex> lock(_mtx);
            _exchanges.clear();
            _exchange_durable.delete_table();
        }

        // 交换机数量(供测试使用)
        size_t size()
        {
            unique_lock<mutex> lock(_mtx);
            return _exchanges.size();
        }

    private:
        ExchangeDurable _exchange_durable;               // 交换机持久化处理
        unordered_map<string, Exchange::ptr> _exchanges; // 存储所有交换机
        mutex _mtx;                                      // 防止多线程同时执行
    };

}